/*
 * Factory.java
 *
 * Created on 06. Juni 2007, 16:58
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package sbc.fact;

import sbc.corso.CorsoConnectionHolder;
import sbc.corso.JobApplicationReceiver;
import sbc.corso.JobApplicationNotifier;
import sbc.repos.*;

import sbc.util.*;
import corso.lang.*;
import java.util.*;
import java.util.concurrent.*;



/**
 *
 * @author mustermark
 */
public class Factory extends CorsoConnectionHolder {//implements CorsoShareable, Identifiable {
    
    /** Creates a new instance of Factory */
    private String company;
    private static Factory defaultFact;
    public final static String DEFAULT_FACTORY_NAME = "ModuleFactory";
    //private CorsoVarOid factOid;
    //private CorsoVarOid staffOid;
    //private CorsoVarOid storageOid;
    private List<CorsoShareable> tasks = Collections.synchronizedList(new ArrayList<CorsoShareable>());
    private List<CorsoShareable> modules = Collections.synchronizedList(new ArrayList<CorsoShareable>());
    //private ConcurrentLinkedQueue<Task> taskQueque = new ConcurrentLinkedQueue<Task>();
    //private ConcurrentLinkedQueue<Module> moduleQueque = new ConcurrentLinkedQueue<Module>();
    private ConcurrentLinkedQueue<Inspection> inspectionQueque = new ConcurrentLinkedQueue<Inspection>();
    private ConcurrentLinkedQueue<Assembly> assemblyQueque = new ConcurrentLinkedQueue<Assembly>();
    private ConcurrentLinkedQueue<Delivery> deliveryQueque = new ConcurrentLinkedQueue<Delivery>();
    
    private ConcurrentLinkedQueue<Sensor> uninspectedSensorQueque = new ConcurrentLinkedQueue<Sensor>();
    private ConcurrentLinkedQueue<Sensor> inspectedSensorQueque = new ConcurrentLinkedQueue<Sensor>();
    private ConcurrentLinkedQueue<Board> boardQueque = new ConcurrentLinkedQueue<Board>();
    private ConcurrentLinkedQueue<Unit> unitQueque = new ConcurrentLinkedQueue<Unit>();
    
    
    //private Storage storage;
    //private Staff staff;
    
          
    /*
     *  create empty Factory
     */
    public Factory(String name, String bridgeStreamName) {
        super();
        company = name;
        //modules = new ArrayList<CorsoShareable>();
        //staffOid = new CorsoVarOid();
        //storageOid = new CorsoVarOid();
        // get the named object if it was created before else create it *******
        CorsoConstOid bridgeStreamOid = null;
        try {
            bridgeStreamOid = con.getNamedConstOid(bridgeStreamName, site, null, false, CorsoConnection.NO_TIMEOUT);
        } catch(CorsoException e) {
            System.out.println("Couldn't retrieve bridgeStream for: " +bridgeStreamName);
            System.exit(-10);
            //throw new BridgeException();

        }


        // create the stream object ********************************************
        //BridgeStream bridgeStreamObj = new BridgeStream(bridgeStreamOid);

        //bridgeStreamObj.appendRawMaterialStreamName(name);
        
        
        
        /*CorsoConstOid rawMaterialOid = con.getOrCreateNamedConstOid (con.getCurrentStrategy(), name, null);
        final RawMaterialStream rawMaterial = new RawMaterialStream(rawMaterialOid);
        Runnable rawMaterialLine = new Runnable() {
            public void run() {
                for (;;) {
                    Module mod = rawMaterial.readNextModule();
                    System.out.println ("New data: " + mod);
                    //moduleQueque.add(mod);
                    if(mod instanceof Sensor) {
                        uninspectedSensorQueque.add((Sensor)mod);
                    } else if(mod instanceof Board) {
                        boardQueque.add((Board)mod);
                    } else {
                        System.out.println("Unknown RawMaterial");
                    }
                }
            }
        };
        Thread rawMaterialLineThread = new Thread(rawMaterialLine);
        rawMaterialLineThread.start();
        System.out.println ("rawmaterialline for " + name + " established!");
*/
        
        Vector users = new Vector ();
        users.addElement (new String ("*"));

        // create new service types ********************************************
        //con.addServiceType ("InspectionExecutionService", "SensorInspection", CorsoConnection.USER_ADMINISTRATED, users, "Desc");
        System.out.println ("Service type 'InspectionExecutionService' created ");
        //con.addServiceType ("AssemblyExecutionService", "UnitAssembly", CorsoConnection.USER_ADMINISTRATED, users, "Desc");
        System.out.println ("Service type 'AssemblyExecutionService' created ");
        //con.addServiceType ("DeliveryExecutionService", "UnitDelivery", CorsoConnection.USER_ADMINISTRATED, users, "Desc");
        System.out.println ("Service type 'DeliveryExecutionService' created ");

        Thread inspectionThread = new Thread(new InspectionExecution());
        inspectionThread.start();
        
        
    }
    
    class InspectionExecution implements Runnable {
        public void run() {
            for (;;) {
                if(inspectionQueque.isEmpty()) continue;
                if(uninspectedSensorQueque.isEmpty()) continue;
                final Inspection inspection = inspectionQueque.poll();
                final Sensor sensor = uninspectedSensorQueque.poll();
                Runnable inspectProcess = new Runnable() {
                    public void run() {
                        try {
                            CorsoVarOid cvoid1 = con.createVarOid (con.getCurrentStrategy());
                            CorsoVarOid cvoid2 = con.createVarOid (con.getCurrentStrategy());
                            // put the oids into CORSO Data object, used as argument for entry *****
                            CorsoData argData = con.createData ();
                            argData.putShareable (cvoid1);
                            argData.putShareable (cvoid2);
                            // start the entry with argument **************************************
                            con.createIndependentProcess (inspection.getUID(), argData, userName, "InspectionExecutionService", site);
                            // write data structure to the first oid ****************************
                            CorsoTopTransaction tx = con.createTopTransaction ();
                            cvoid1.writeShareable(sensor, tx);
                            tx.commit (CorsoConnection.INFINITE_TIMEOUT);

                            // waiting until second oid is written ******************************
                            cvoid2.readShareable (sensor, null, CorsoConnection.INFINITE_TIMEOUT);
                            System.out.println ("-----------------------------------");
                            System.out.println ("inspection finished for: " + sensor);

                            boolean allChecked = true;
                            for(Feature f : sensor.getFeatures()) {
                                if(!sensor.isInspected(f)) {
                                    allChecked = false;
                                    break;
                                }
                            }

                            if(allChecked) {
                                System.out.println("sensor " + sensor + " ready for Assembly");
                                inspectedSensorQueque.add(sensor);
                            }
                            // wait for a moment so that entry Sample7Entry can finish ************
                            //Thread.currentThread ().sleep (750);
                            //connection.disconnect ();
                        } catch(CorsoException e) {
                            System.out.println("Error running Inpection on " + sensor);
                            System.out.println(e);
                            //continue execution;
                        }

                    }
                };
                Thread t = new Thread(inspectProcess);
                t.start();
            }

        }
    }
    
    class DeliveryExecution implements Runnable {
        public void run() {
            for (;;) {
                if(deliveryQueque.isEmpty()) continue;
                if(unitQueque.isEmpty()) continue;
                final Delivery delivery = deliveryQueque.poll();
                final Unit unit = unitQueque.poll();
                Runnable deliveryProcess = new Runnable() {
                    public void run() {
                        try {
                            CorsoVarOid cvoid1 = con.createVarOid (con.getCurrentStrategy());
                            CorsoVarOid cvoid2 = con.createVarOid (con.getCurrentStrategy());
                            // put the oids into CORSO Data object, used as argument for entry *****
                            CorsoData argData = con.createData ();
                            argData.putShareable (cvoid1);
                            argData.putShareable (cvoid2);
                            // start the entry with argument **************************************
                            //con.createIndependentProcess (delivery.getUID(), argData, userName, "DeliveryExecutionService", site);
                            // write data structure to the first oid ****************************
                            CorsoTopTransaction tx = con.createTopTransaction ();
                            cvoid1.writeShareable(unit, tx);
                            tx.commit (CorsoConnection.INFINITE_TIMEOUT);

                            System.out.println ("delivered: " + unit);

                            
                            //connection.disconnect ();
                        } catch(CorsoException e) {
                            System.out.println("Error running Delivery on " + unit);
                            System.out.println(e);
                            //continue execution;
                        }

                    }
                };
                Thread t = new Thread(deliveryProcess);
                t.start();
            }
            // wait for a moment so that entry Sample7Entry can finish ************
            //Thread.currentThread ().sleep (750);
                            

        }
    }
    
    class AssemblyExecution implements Runnable {
        public void run() {
            for (;;) {
                if(assemblyQueque.isEmpty()) continue;
                if(inspectedSensorQueque.size() < 2) continue;
                if(boardQueque.isEmpty()) continue;
                final Assembly assembly = assemblyQueque.poll();
                final Sensor sensor1 = inspectedSensorQueque.poll();
                final Sensor sensor2 = inspectedSensorQueque.poll();
                final Board board =  boardQueque.poll();
                
                Runnable assemblyProcess = new Runnable() {
                    public void run() {
                        try {
                            CorsoVarOid cvoid1 = con.createVarOid (con.getCurrentStrategy());
                            CorsoVarOid cvoid2 = con.createVarOid (con.getCurrentStrategy());
                            // put the oids into CORSO Data object, used as argument for entry *****
                            CorsoData argData = con.createData ();
                            argData.putShareable (cvoid1);
                            argData.putShareable (cvoid2);
                            // start the entry with argument **************************************
                            con.createIndependentProcess (assembly.getUID(), argData, userName, "AssemblyExecutionService", site);
                            // write data structure to the first oid ****************************
                            CorsoTopTransaction tx = con.createTopTransaction ();
                            Unit unit = new Unit(board, sensor1, sensor2);
                            cvoid1.writeShareable(unit, tx);
                            //cvoid1.writeShareable(sensor1, tx);
                            //cvoid1.writeShareable(sensor2, tx);
                            
                            tx.commit (CorsoConnection.INFINITE_TIMEOUT);

                            // waiting until second oid is written ******************************
                            cvoid2.readShareable (unit, null, CorsoConnection.INFINITE_TIMEOUT);
                            System.out.println ("-----------------------------------");
                            System.out.println ("assembly finished for: " + unit);

                            unitQueque.add(unit);
                            //connection.disconnect ();
                        } catch(CorsoException e) {
                            System.out.println("Error running Assembly on " + board + ", " + sensor1 + ", " + sensor2);
                            System.out.println(e);
                            //continue execution;
                        }

                    }
                };
                
                Thread t = new Thread(assemblyProcess);
                t.start();
            }
            
            // wait for a moment so that assembly can finish ************            
            //Thread.currentThread ().sleep (750);
                            
        }
    }
    
    
    public String getUID() { return company; }
    
    /*JobApplicationNotifier assemblyNotifier = new JobApplicationNotifier("AssemblyRegistration", con, 
            new JobApplicationReceiver(){
                public void shareableRegistered(CorsoShareable shareable) {
                    assemblyQueque.add((Assembly)shareable);
                }
            }
    ) {
        protected Task createEmptyShareable() {
            return new Assembly();
        }
    };*/
    /*JobApplicationNotifier deliveryNotifier = new JobApplicationNotifier("DeliveryRegistration", con, 
            new JobApplicationReceiver(){
                public void shareableRegistered(CorsoShareable shareable) {
                    deliveryQueque.add((Delivery)shareable);
                }
            }
    ) {
        protected Task createEmptyShareable() {
            return new Delivery();
        }
    };*/
    
    public static void main (String [] args)
    {
        if (args.length != 2)
        {
            System.out.println ("CALL: Factory <name> <bridgeName>");
            System.exit (-1);
        }

        Factory f = new Factory(args[0], args[1]);
        //f.open();
    }
    
     
    
}
